package mobi.yuanshen.utils;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 该类主要是把文件存储到服务器上的工具类。
 * 该类提供了将MultipartFile对象上传到服务器的方法。
 * public static String transToServer (String requestUrl,@NonNull MultipartFile file,@NonNull String fileName)方法
 * 提供了一种不经过转存，直接转存到服务器的提交方式，可以节省中专服务器的硬盘和内存资源，较推荐。
 * 需要注意的是transToServer的第一个参数如果不指定的话，
 * 会使用ParamsUtils.FILE_UPLOAD_INTERFACE_URL值作为默认的接口url。
 * @Date 2025-05-23 20:47
 */
@Slf4j
@Component
public class FileTransforServer {

    @Autowired
    private ParamsUtils paramsUtils; // 注入参数工具类

    /**
     * 将流直接向服务器存储，不经过本地转存，直接用这个就好
     * @param requestUrl 文件url
     * @param file MultipartFile文件
     * @param fileName 指定文件名
     * @return 服务器存储成功后返回的文件名
     * @throws IOException
     */
    public String transToServer (String requestUrl,@NonNull MultipartFile file,@NonNull String fileName) throws IOException {
        RestTemplate restTemplate = new RestTemplate(); // 创建template对象
        if (requestUrl == null) { // 若未传递requestUrl的值，则给与默认值
            requestUrl = paramsUtils.FILE_UPLOAD_INTERFACE_URL;
        }
        log.info("请求url:" + requestUrl);
        MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>(); // 请求体表单
        parts.add("file", new InputStreamResource(file.getInputStream()) {
            // 这里一定要重写这两个方法，不然会报错的
            @Override
            public long contentLength() throws IOException {
                // 返回流的大小
                return file.getSize();
            }

            @Override
            public String getFilename() {
                // 返回文件名
                return fileName;
            }
        });//将数据添加到表单
        HttpHeaders headers = new HttpHeaders(); // 请求头
        headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 设置请求数据的格式为formData
        headers.set("AccessKey",paramsUtils.FileUploadAccessKey);
        HttpEntity httpEntity = new HttpEntity(parts,headers); // 创建http实体对象
        try {
            Result res = restTemplate.postForObject(
                    requestUrl, // 请求url
                    httpEntity, // http请求对象
                    Result.class); // 返回值的类型
            log.info("服务器返回数据 -- >{}", res);
            return res.getData().toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * 内部类，实现了Callable接口，用于开启多线程。意义不大
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Deprecated
    public class ThreadToTrans implements Callable<String> {
        private String requestUrl;
        private String filePath;
        @Override
        public String call() throws Exception {
            return transToServer(requestUrl,filePath);
        }
    }
    /**
     * 封装的最顶级方法，一键上传MultipartFile文件，有替代方案，弃用！
     * @param file
     * @return 服务器存储成功后的文件名
     * @throws IOException
     */
    @Deprecated // 弃用
    public String StratUploadFile(MultipartFile file) throws IOException {
        String[] split = file.getOriginalFilename().split("\\.");//分离扩展名
        //加时间戳(double)
        String newFIleName
                = split[0] + "_" + System.currentTimeMillis() + "." +  split[split.length - 1];
        File localSavePath = new File(paramsUtils.LocalFileStorageAbsolutePath,newFIleName);
        file.transferTo(localSavePath);
        log.info("成功转存到本地:" + localSavePath + "开始向服务器转存...");
        String name = transToServer(null,localSavePath.toString());
        log.info("转存成功,文件名:"+ name);
        log.info("文件访问链接:" + paramsUtils.FileStorageHttpPath + "/" + name);
        log.info("删除本地缓存文件" + localSavePath);
        localSavePath.delete();
        log.info("删除成功...");
        return name;
    }
    /**
     * 开启一个新线程去存储数据，因为用到了CallAble接口，所以其实意义不大，自嗨代码
     * @param requestUrl 请求路径
     * @param filePath 文件路径
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @Deprecated // 弃用
    public String newThreadToTrans (String requestUrl,String filePath)
            throws ExecutionException, InterruptedException {
        ThreadToTrans threadToTrans = new ThreadToTrans(requestUrl,filePath);
        FutureTask<String> futureTask = new FutureTask<>(threadToTrans);
        Thread thread = new Thread(futureTask);
        thread.start(); // 开启线程
        return futureTask.get(); //get会导致阻塞，其实这个多线程的意义不大
    }
    /**
     * 将文件转存到本地后再向服务器请求转存文件,弃用,有替代方案
     * @param requestUrl 请求url
     * @param filePath 要转存的文件
     * @return
     */
    @Deprecated // 弃用
    public String transToServer (String requestUrl,String filePath) {
        RestTemplate restTemplate = new RestTemplate(); // 创建template对象
        if (requestUrl == null) { // 若未传递requestUrl的值，则给与默认值
            requestUrl = paramsUtils.FILE_UPLOAD_INTERFACE_URL;
        }
        log.info("请求url:" + requestUrl);
        MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>(); // 请求体
        parts.add("file", new FileSystemResource(filePath));
        HttpHeaders headers = new HttpHeaders(); // 请求头
        headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 设置请求数据的格式为formData
        HttpEntity httpEntity = new HttpEntity(parts,headers); // 创建http实体对象
        try {
            Result res = restTemplate.postForObject(
                    requestUrl, // 请求url
                    httpEntity, // http请求对象
                    Result.class); // 返回值的类型
            System.out.println("服务器返回数据 -- >" + res);
            return res.getData().toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
